﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace MrPainter
{
    struct Line
    {
        public Vector3 start;
        public Vector3 end;
    }

    class PlayerLine
    {
        List<Line> lines;    // Lista de structs que armazenam as linhas para testes de colisão.
        Line2D originalLine; // Linha usada durante a checagem de colisão
        Line2D originalLineNormalised; // Linha atual normalizada
        Line2D startLineToCircle; //Início da linha atual até o circulo
        Vector3 startPoint;  // Ponto em que o jogador clicou pela primeira vez ao criar a linha.

        double originalLineLength; //Tamanho original da linha atual
        double lineProjectionLength; //Projeção do círculo sobre a linha

        VertexPositionColor[] line;           // Array representando a linha atual
        VertexPositionColor[] tempLine;       // Array representando a futura linha
        VertexPositionColor currentVertice;   // Último vertice criado

        bool isNewLine;       // Booleano que diz se uma nova linha foi criada.
        bool isFirstClick;    // Checa se o jogador fez o primeiro clique para criar a linha.

        BasicEffect basicEffect;  // Viewport

        GraphicsDeviceManager graphics;
        GraphicsDevice graphicsDevice;

        public PlayerLine(GraphicsDeviceManager graphics, GraphicsDevice graphicsDevice)
        {
            this.graphics = graphics;
            this.graphicsDevice = graphicsDevice;

            isNewLine = false;
            isFirstClick = true;
            lines = new List<Line>();
            line = new VertexPositionColor[2];
            tempLine = new VertexPositionColor[2];

            basicEffect = new BasicEffect(graphicsDevice);
            basicEffect.VertexColorEnabled = true;
            basicEffect.Projection = Matrix.CreateOrthographicOffCenter
               (0, graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height, 0, 0, 1);
        }

        public void Update(GameTime gameTime, Player player, MouseState mouse, MouseState last_mouse)
        {
            //Criação de linhas
            if (mouse.LeftButton == ButtonState.Pressed && last_mouse.LeftButton == ButtonState.Released)
            {
                if (isFirstClick)
                {
                    isFirstClick = false;
                    tempLine = new VertexPositionColor[2];

                    startPoint = new Vector3(mouse.X, mouse.Y, 0);
                    currentVertice.Position = startPoint;
                    currentVertice.Color = Color.Black;
                    tempLine[0] = currentVertice;
                    tempLine[1] = currentVertice;
                }
                else
                {
                    Line newLine = new Line();
                    newLine.start = startPoint;
                    newLine.end = new Vector3(mouse.X, mouse.Y, 0);
                    lines.Add(newLine);

                    currentVertice.Position = newLine.end;
                    currentVertice.Color = Color.Black;
                    tempLine[1] = currentVertice;

                    startPoint = Vector3.Zero;
                    isNewLine = true;
                    isFirstClick = true;
                }
            }
            else
            {
                if (!isFirstClick)
                {
                    currentVertice.Position = new Vector3(mouse.X, mouse.Y, 0);
                    currentVertice.Color = Color.Black;
                    tempLine[1] = currentVertice;
                }

            }

            //Checa se o número de linhas ultrapassa o permitido, se ultrapassar remove a primeira linha criada.
            if (isNewLine)
            {
                if (lines.Count > 1)
                    lines.RemoveAt(0);

                line = tempLine;
                tempLine = null;

                isNewLine = false;

                originalLine = new Line2D(lines[0].end.X - lines[0].start.X, lines[0].end.Y - lines[0].start.Y);
                originalLineLength = originalLine.getMagnitude();
                originalLineNormalised = originalLine.normalise();
            }

            // Checagem de colisão com as linhas
            if (lines.Count > 0)
            {
                startLineToCircle = new Line2D(player.center.X - lines[0].start.X, player.center.Y - lines[0].start.Y);
                lineProjectionLength = startLineToCircle.dotProduct(originalLineNormalised);

                if (lineProjectionLength > -player.radius && lineProjectionLength < (originalLineLength + player.radius))
                {
                    Line2D lineProjection = originalLineNormalised.scale(lineProjectionLength);
                    Vector3 closestPoint = new Vector3((float)(lines[0].start.X + lineProjection.x),
                                                        (float)(lines[0].start.Y + lineProjection.y), 0);

                    Line2D playerToLine = new Line2D(player.center.X - closestPoint.X, player.center.Y - closestPoint.Y);
                    double distance = playerToLine.getMagnitude();

                    if (distance < player.radius)
                    {
                        //Fazendo a bola encostar na linha (ela atravessou a linha neste momento)
                        double diff = player.radius - distance;
                        Line2D distanceNormalised = playerToLine.normalise();
                        Vector2 offset = new Vector2((float)(distanceNormalised.x * diff), (float)(distanceNormalised.y * diff));
                        player.center = new Vector3(player.center.X + offset.X, player.center.Y + offset.Y, 0);
                        player.position = new Vector2(player.center.X - (float)player.radius, player.center.Y - (float)player.radius);


                        //Setando o novo vetor velocidade
                        float vx = player.velocity.X;
                        float vy = player.velocity.Y;
                        float a;
                        float b;

                        if ((lines[0].end.X >= lines[0].start.X) && player.velocity.X < 0 ||
                            (lines[0].end.X < lines[0].start.X) && player.velocity.X > 0)
                        {
                            a = lines[0].start.X - lines[0].end.X;
                            b = lines[0].start.Y - lines[0].end.Y;
                        }
                        else
                        {
                            a = lines[0].end.X - lines[0].start.X;
                            b = lines[0].end.Y - lines[0].start.Y;
                        }


                        float newX = (vx * ((a * a) - (b * b)) + (2 * vy * a * b)) / ((a * a) + (b * b));
                        float newY = ((2 * vx * a * b) - vy * ((a * a) - (b * b))) / ((a * a) + (b * b));

                        player.velocity.X = newX * 0.9f;
                        player.velocity.Y = newY * 0.9f;

                    }
                }
            }
            
        }

        public void Draw()
        {
            basicEffect.CurrentTechnique.Passes[0].Apply(); //Vertex e Pixel Shader

            if (lines.Count > 0)
                graphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, line, 0, 1);

            if (tempLine != null)
                graphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, tempLine, 0, 1);
        }
    }
}
